/*******************************************************************************
  * 文件：UartComm_Driver.c
  * 作者：zyz
  * 版本：v1.0.0
  * 日期：2017-08-03
  * 说明：通信驱动
*******************************************************************************/
/* 头文件 *********************************************************************/
#include "UartComm_Driver.h"
#include "Hardware.h"
#include "Hardware_Uart.h"
#include "OS_Task.h"
#include "CRC.h"
#include "UartComm_DispatchMsg.h"

/* 宏定义 *********************************************************************/
/* 类型定义 *******************************************************************/
/* 变量定义 *******************************************************************/
static UartComm_ts sUartComm;    // 串口通信控制

/* 函数声明 *******************************************************************/
static Bool UartComm_IsSpecialChar(U8 u8Byte);                   // 判断特殊字符
static U8 UartComm_GetLocalSeqNum(UartCommPort_te ePort);        // 获取本地序列号
static void UartComm_UpdateLocalSeqNum(UartCommPort_te ePort);   // 更新本地序列号
static void UartComm_CheckSendRequest(UartCommPort_te ePort);    // 检查发送请求
static void UartComm_SendDelayTimerCallback(U32 u32Parm);        // 发送延时回调函数
static void UartComm_ParseRecvPacket(U32 u32Parm);               // 解析接收数据包

/* 函数定义 *******************************************************************/

/*******************************************************************************
  * 函数名：UartComm_InitDriver
  * 功  能：初始化驱动
  * 参  数：无
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void UartComm_InitDriver(void)
{
    U8 u8Index;
    UartCommControl_ts *psCommCtrl = sUartComm.asCommControl;

    // 初始化端口
    psCommCtrl[ePORT_DISPLAY_BOARD].pfSendByte = Hardware_SendCommByte;
    psCommCtrl[ePORT_DISPLAY_BOARD].pfRecvByte = Hardware_RecvCommByte;
    // 初始化通信
    for(u8Index=0; u8Index<ePORT_SUM; u8Index++)
    {
        // 初始化序列号
        psCommCtrl[u8Index].u8LocalSeqNum = u8UARTCOMM_MIN_SEQ_NUM;
        // 初始化发送和接收状态
        psCommCtrl[u8Index].sSendControl.eState = eSTATE_SEND_IDLE;
        psCommCtrl[u8Index].sRecvControl.eState = eSTATE_WAITING_STX;
        // 初始化发送和接收队列
        Queue_Init(&(psCommCtrl[u8Index].sSendQueue), psCommCtrl[u8Index].asSendBuffer,
                   u8UARTCOMM_SEND_QUEUE_SIZE, sizeof(UartCommPacket_ts));
        Queue_Init(&(psCommCtrl[u8Index].sRecvQueue), psCommCtrl[u8Index].asRecvBuffer,
                   u8UARTCOMM_RECV_QUEUE_SIZE, sizeof(UartCommPacket_ts));
    }
}

/*******************************************************************************
  * 函数名：UartComm_IsSpecialChar
  * 功  能：判断特殊字符
  * 参  数：u8Byte - 字节
  * 返回值：结果
  * 说  明：无
*******************************************************************************/
static Bool UartComm_IsSpecialChar(U8 u8Byte)
{
    Bool bResult = FALSE;

    // 是特殊字符
    if((u8Byte == u8UARTCOMM_PKT_ESC) ||
       (u8Byte == u8UARTCOMM_PKT_ACK) ||
       (u8Byte == u8UARTCOMM_PKT_STX) ||
       (u8Byte == u8UARTCOMM_PKT_ETX))
    {
        bResult = TRUE;
    }
    // 返回结果
    return bResult;
}

/*******************************************************************************
  * 函数名：UartComm_GetLocalSeqNum
  * 功  能：获取本地序列号
  * 参  数：ePort - 端口
  * 返回值：本地序列号
  * 说  明：无
*******************************************************************************/
static U8 UartComm_GetLocalSeqNum(UartCommPort_te ePort)
{
    UartCommControl_ts *psCommCtrl = &(sUartComm.asCommControl[ePort]);

    // 检查序列号
    if(psCommCtrl->u8LocalSeqNum > u8UARTCOMM_MAX_SEQ_NUM)
    {
        psCommCtrl->u8LocalSeqNum = u8UARTCOMM_MIN_SEQ_NUM;
    }

    // 返回序列号
    return (psCommCtrl->u8LocalSeqNum);
}

/*******************************************************************************
  * 函数名：UartComm_UpdateLocalSeqNum
  * 功  能：更新本地序列号
  * 参  数：ePort - 端口
  * 返回值：无
  * 说  明：无
*******************************************************************************/
static void UartComm_UpdateLocalSeqNum(UartCommPort_te ePort)
{
    UartCommControl_ts *psCommCtrl = &(sUartComm.asCommControl[ePort]);

    // 更新序列号
    psCommCtrl->u8LocalSeqNum++;
    if(psCommCtrl->u8LocalSeqNum > u8UARTCOMM_MAX_SEQ_NUM)
    {
        psCommCtrl->u8LocalSeqNum = u8UARTCOMM_MIN_SEQ_NUM;
    }
}

/*******************************************************************************
  * 函数名：UartComm_SetupSendPacket
  * 功  能：构建发送数据包
  * 参  数：u8SeqNum  - 序列号
  *         u8DstAddr - 目的地址
  *         u8Command - 命令
  * 返回值：数据起始地址
  * 说  明：与UartComm_SendPacket函数配合使用
  *         u8SeqNum为0表示使用本地序列号，
  *         并在UartComm_SendPacket中更新
*******************************************************************************/
U8* UartComm_SetupSendPacket(U8 u8SeqNum, U8 u8DstAddr, U8 u8Command)
{
    UartCommPacket_ts *psSendPacket = &(sUartComm.sSendPacket);

    // 填充序列号，地址，命令字段
    psSendPacket->u8SeqNum  = u8SeqNum;
    psSendPacket->u8SrcAddr = u8UARTCOMM_LOCAL_ADDR;
    psSendPacket->u8DstAddr = u8DstAddr;
    psSendPacket->u8Command = u8Command;
    // 返回数据起始地址
    return (U8*)(&(psSendPacket->uData));
}

/*******************************************************************************
  * 函数名：UartComm_SendPacket
  * 功  能：发送数据包
  * 参  数：ePort      - 端口
  *         pu8DataEnd - 数据结束指针
  * 返回值：无
  * 说  明：与UartComm_SetupSendPacket函数配合使用
*******************************************************************************/
void UartComm_SendPacket(UartCommPort_te ePort, U8 *pu8DataEnd)
{
    U8 u8Index;
    Bool bUseLocalSeqNum = FALSE;
    UartCommControl_ts *psCommCtrl = sUartComm.asCommControl;
    UartCommPacket_ts *psSendPacket = &(sUartComm.sSendPacket);

    // 计算数据长度
    psSendPacket->u8DataLen = pu8DataEnd - (U8*)(&(psSendPacket->uData));
    // 确保数据长度不大于最大长度
    if(psSendPacket->u8DataLen <= u8UARTCOMM_MAX_DATA_LEN)
    {
        // 序列号为0表示使用本地序列号
        if(psSendPacket->u8SeqNum == 0)
        {
            bUseLocalSeqNum = TRUE;
        }

        // 发送到所有端口
        if(ePort == ePORT_SUM)
        {
            // 广播数据包
            for(u8Index=0; u8Index<ePORT_SUM; u8Index++)
            {
                Bool bResult = FALSE;
                U16ToU8_tu uCRC;

                // 使用本地序列号
                if(bUseLocalSeqNum)
                {
                    psSendPacket->u8SeqNum =
                        UartComm_GetLocalSeqNum((UartCommPort_te)u8Index);
                }

                // 计算CRC16
                uCRC.u16Word = CRC_CalcValue(&(psSendPacket->u8SeqNum),
                                             psSendPacket->u8DataLen +
                                             u8UARTCOMM_SEQ_SRC_DST_CMD_LEN_LEN,
                                             u16UARTCOMM_PKT_CRC_SEED);
                psSendPacket->uData.sComMsgData.au8Data[psSendPacket->u8DataLen] = uCRC.sByte.u8Msb;
                psSendPacket->uData.sComMsgData.au8Data[psSendPacket->u8DataLen + 1] = uCRC.sByte.u8Lsb;

                // 添加到发送队列
                Hardware_EnterCritical();    // 进入临界区
                bResult = Queue_Add(&(psCommCtrl[u8Index].sSendQueue), psSendPacket);
                Hardware_ExitCritical();     // 退出临界区

                // 添加成功
                if(bResult)
                {
                    // 更新本地序列号
                    if(bUseLocalSeqNum)
                    {
                        UartComm_UpdateLocalSeqNum((UartCommPort_te)u8Index);
                    }
                    // 检查发送请求
                    UartComm_CheckSendRequest((UartCommPort_te)u8Index);
                }
            }
        }
        // 发送到指定端口
        else
        {
            Bool bResult = FALSE;
            U16ToU8_tu uCRC;

            // 使用本地序列号
            if(bUseLocalSeqNum)
            {
                psSendPacket->u8SeqNum = UartComm_GetLocalSeqNum(ePort);
            }

            // 计算CRC16
            uCRC.u16Word = CRC_CalcValue(&(psSendPacket->u8SeqNum),
                                         psSendPacket->u8DataLen +
                                         u8UARTCOMM_SEQ_SRC_DST_CMD_LEN_LEN,
                                         u16UARTCOMM_PKT_CRC_SEED);
            psSendPacket->uData.sComMsgData.au8Data[psSendPacket->u8DataLen] = uCRC.sByte.u8Msb;
            psSendPacket->uData.sComMsgData.au8Data[psSendPacket->u8DataLen + 1] = uCRC.sByte.u8Lsb;

            // 添加到发送队列
            Hardware_EnterCritical();    // 进入临界区
            bResult = Queue_Add(&(psCommCtrl[ePort].sSendQueue), psSendPacket);
            Hardware_ExitCritical();     // 退出临界区

            // 添加成功
            if(bResult)
            {
                // 更新本地序列号
                if(bUseLocalSeqNum)
                {
                    UartComm_UpdateLocalSeqNum(ePort);
                }
                // 检查发送请求
                UartComm_CheckSendRequest(ePort);
            }
        }
    }
}

/*******************************************************************************
  * 函数名：UartComm_CheckSendRequest
  * 功  能：检查发送请求
  * 参  数：ePort - 端口
  * 返回值：无
  * 说  明：无
*******************************************************************************/
static void UartComm_CheckSendRequest(UartCommPort_te ePort)
{
    Bool bResult = FALSE;
    UartCommControl_ts *psCommCtrl = &(sUartComm.asCommControl[ePort]);
    UartCommSendControl_ts *psSendCtrl = &(psCommCtrl->sSendControl);

    // 发送空闲
    if(psSendCtrl->eState == eSTATE_SEND_IDLE)
    {
        // 从发送队列中取数据包
        if(psCommCtrl->sSendQueue.u16ElementSize > 0)
        {
            Hardware_EnterCritical();    // 进入临界区
            bResult = Queue_Remove(&(psCommCtrl->sSendQueue),
                                     &(psSendCtrl->sPacket));
            Hardware_ExitCritical();     // 退出临界区
        }

        // 取到数据包，开始发送
        if(bResult == TRUE)
        {
            // 初始化发送控制参数
            psSendCtrl->u8SendByteCount = 0;
            psSendCtrl->bEscCharActive = FALSE;
            psSendCtrl->pu8SendByte = &(psSendCtrl->sPacket.u8SeqNum);
            // 更新发送状态，发送STX
            psSendCtrl->eState = eSTATE_SENT_STX;
            psCommCtrl->pfSendByte(u8UARTCOMM_PKT_STX);
        }
    }
}

/*******************************************************************************
  * 函数名：UartComm_SendByteHandler
  * 功  能：发送字节处理
  * 参  数：ePort - 端口
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void UartComm_SendByteHandler(UartCommPort_te ePort)
{
    UartCommControl_ts *psCommCtrl = &(sUartComm.asCommControl[ePort]);
    UartCommSendControl_ts *psSendCtrl = &(psCommCtrl->sSendControl);

    // 已发送帧头，发送帧体
    if(psSendCtrl->eState == eSTATE_SENT_STX)
    {
        // 转义字符无效
        if(psSendCtrl->bEscCharActive == FALSE)
        {
            // 发送字节是特殊字符
            if(UartComm_IsSpecialChar(*(psSendCtrl->pu8SendByte)) == TRUE)
            {
                // 设置转义字符有效标志，发送转义字符
                psSendCtrl->bEscCharActive = TRUE;
                psCommCtrl->pfSendByte(u8UARTCOMM_PKT_ESC);
            }
            // 发送字节不是特殊字符
            else
            {
                // 发送字节，更新发送字节计数
                psCommCtrl->pfSendByte(*(psSendCtrl->pu8SendByte++));
                psSendCtrl->u8SendByteCount++;
            }
        }
        // 转义字符有效
        else
        {
            // 清除转义字符有效标志
            psSendCtrl->bEscCharActive = FALSE;
            // 发送字节，更新发送字节计数
            psCommCtrl->pfSendByte(*(psSendCtrl->pu8SendByte++));
            psSendCtrl->u8SendByteCount++;
        }

        // 发送完帧体
        if(psSendCtrl->u8SendByteCount >=
           (psSendCtrl->sPacket.u8DataLen +
            u8UARTCOMM_SEQ_SRC_DST_CMD_LEN_CRC_LEN))
        {
            // 更新发送状态，已发送帧体
            psSendCtrl->eState = eSTATE_SENT_BODY;
        }
    }
    // 已发送帧体，发送帧尾
    else if(psSendCtrl->eState == eSTATE_SENT_BODY)
    {
        // 发送帧尾，更新发送状态
        psCommCtrl->pfSendByte(u8UARTCOMM_PKT_ETX);
        psSendCtrl->eState = eSTATE_SENT_ETX;
    }
    // 已发送帧尾，发送完成
    else if(psSendCtrl->eState == eSTATE_SENT_ETX)
    {
        // 开启发送延时定时器
        OS_TimerRestartEx(&(psSendCtrl->sSendTimer),
                          u16UARTCOMM_SEND_INTERVAL_MSEC,
                          UartComm_SendDelayTimerCallback,
                          (U32)ePort);
    }
    // 其他状态
    else
    {
        // 重置发送状态，检查发送请求
        psSendCtrl->eState = eSTATE_SEND_IDLE;
        UartComm_CheckSendRequest(ePort);
    }
}

/*******************************************************************************
  * 函数名：UartComm_SendDelayTimerCallback
  * 功  能：发送延时回调函数
  * 参  数：u32Parm - 参数，这里传递端口
  * 返回值：无
  * 说  明：无
*******************************************************************************/
static void UartComm_SendDelayTimerCallback(U32 u32Parm)
{
    UartCommPort_te ePort;
    UartCommSendControl_ts *psSendCtrl;

    if(u32Parm < (U32)ePORT_SUM)
    {
        ePort = (UartCommPort_te)u32Parm;
        psSendCtrl = &(sUartComm.asCommControl[ePort].sSendControl);

        // 停止定时器
        OS_TimerStop(&(psSendCtrl->sSendTimer));
        // 重置发送状态，检查发送请求
        psSendCtrl->eState = eSTATE_SEND_IDLE;
        UartComm_CheckSendRequest(ePort);
    }
}

/*******************************************************************************
  * 函数名：UartComm_RecvByteHandler
  * 功  能：接收字节处理
  * 参  数：ePort - 端口
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void UartComm_RecvByteHandler(UartCommPort_te ePort)
{
    U8 u8Index;
    U8 u8RecvByte;
    UartCommControl_ts *psCommCtrl = &(sUartComm.asCommControl[ePort]);
    UartCommRecvControl_ts *psRecvCtrl = &(psCommCtrl->sRecvControl);

    // 获取接收字节
    u8RecvByte = psCommCtrl->pfRecvByte();

    // 等待帧头
    if(psRecvCtrl->eState == eSTATE_WAITING_STX)
    {
        // 接收到帧头
        if(u8RecvByte == u8UARTCOMM_PKT_STX)
        {
            // 初始化接收控制参数
            psRecvCtrl->u8RecvByteCount = 0;
            psRecvCtrl->bEscCharActive = FALSE;
            psRecvCtrl->pu8RecvPos = &(psRecvCtrl->sPacket.u8SeqNum);
            psRecvCtrl->sPacket.u8DataLen = u8UARTCOMM_MAX_DATA_LEN;
            // 更新接收状态为接收帧体
            psRecvCtrl->eState = eSTATE_RECEIVING_BODY;
        }
    }
    // 接收帧体
    else if(psRecvCtrl->eState == eSTATE_RECEIVING_BODY)
    {
        // 转义字符无效
        if(psRecvCtrl->bEscCharActive == FALSE)
        {
            // 接收字节是帧头，重新接收帧体
            if(u8RecvByte == u8UARTCOMM_PKT_STX)
            {
                // 初始化接收控制参数
                psRecvCtrl->u8RecvByteCount = 0;
                psRecvCtrl->bEscCharActive = FALSE;
                psRecvCtrl->pu8RecvPos = &(psRecvCtrl->sPacket.u8SeqNum);
                psRecvCtrl->sPacket.u8DataLen = u8UARTCOMM_MAX_DATA_LEN;
                // 更新接收状态为接收帧体
                psRecvCtrl->eState = eSTATE_RECEIVING_BODY;
            }
            // 接收字节是转义字符
            else if(u8RecvByte == u8UARTCOMM_PKT_ESC)
            {
                // 设置转义字符有效标志
                psRecvCtrl->bEscCharActive = TRUE;
            }
            // 接收字节是其他特殊字符，则接收错误
            else if(UartComm_IsSpecialChar(u8RecvByte) == TRUE)
            {
                // 重置接收状态
                psRecvCtrl->eState = eSTATE_WAITING_STX;
            }
            // 接收字节不是特殊字符
            else
            {
                // 保存接收字节，更新接收字节数
                *(psRecvCtrl->pu8RecvPos++) = u8RecvByte;
                psRecvCtrl->u8RecvByteCount++;
            }
        }
        // 转义字符有效
        else
        {
            // 接收字节是特殊字符
            if(UartComm_IsSpecialChar(u8RecvByte) == TRUE)
            {
                // 清除转义字符有效标志
                psRecvCtrl->bEscCharActive = FALSE;
                // 保存接收字节，更新接收字节数
                *(psRecvCtrl->pu8RecvPos++) = u8RecvByte;
                psRecvCtrl->u8RecvByteCount++;
            }
            // 接收字节不是特殊字符,则接收错误
            else
            {
                // 重置接收状态
                psRecvCtrl->eState = eSTATE_WAITING_STX;
            }
        }

        // 接收完帧体
        if(psRecvCtrl->u8RecvByteCount >=
           (psRecvCtrl->sPacket.u8DataLen +
            u8UARTCOMM_SEQ_SRC_DST_CMD_LEN_CRC_LEN))
        {
            // 更新接收状态为等待帧尾
            psRecvCtrl->eState = eSTATE_WAITING_ETX;
        }
        // 未接收完帧体，但接收字节数达到最大长度
        else if(psRecvCtrl->u8RecvByteCount >= u8UARTCOMM_MAX_BODY_LEN)
        {
            // 数据包超长，重置接收状态
            psRecvCtrl->eState = eSTATE_WAITING_STX;
        }
    }
    // 等待帧尾
    else if(psRecvCtrl->eState == eSTATE_WAITING_ETX)
    {
        // 接收完成，重置接收状态
        psRecvCtrl->eState = eSTATE_WAITING_STX;

        // 接收字节是帧尾
        if(u8RecvByte == u8UARTCOMM_PKT_ETX)
        {
            // 目的地址是本地地址
            if(psRecvCtrl->sPacket.u8DstAddr == u8UARTCOMM_LOCAL_ADDR)
            {
                // 添加接收数据包到接收队列，添加解析任务
                Queue_Add(&(psCommCtrl->sRecvQueue), &(psRecvCtrl->sPacket));
                OS_TaskAddEx(UartComm_ParseRecvPacket, (U32)ePort);
            }
            // 目的地址是广播地址
            else if (psRecvCtrl->sPacket.u8DstAddr == u8UARTCOMM_BROADCAST_ADDR)
            {
                // 进行消息转发
                for(u8Index=0; u8Index<ePORT_SUM; u8Index++)
                {
                    // 向其他端口转发
                    if((UartCommPort_te)u8Index != ePort)
                    {
                        // 添加到发送队列，检查发送请求
                        Queue_Add(&(sUartComm.asCommControl[u8Index].sSendQueue),
                                  &(psRecvCtrl->sPacket));
                        UartComm_CheckSendRequest((UartCommPort_te)u8Index);
                    }
                }
                // 添加接收数据包到接收队列，添加解析任务
                Queue_Add(&(psCommCtrl->sRecvQueue), &(psRecvCtrl->sPacket));
                OS_TaskAddEx(UartComm_ParseRecvPacket, (U32)ePort);
            }
            // 目的地址是其他地址
            else
            {
                Bool bResult;
                UartCommPort_te eDesPort;

                // 查找端口
                bResult = UartComm_LookupPort(psRecvCtrl->sPacket.u8DstAddr,
                                              &eDesPort);
                // 找到端口，进行转发
                if((bResult == TRUE) && (eDesPort != ePort) && (eDesPort != ePORT_SUM))
                {
                    // 添加到发送队列，检查发送请求
                    Queue_Add(&(sUartComm.asCommControl[eDesPort].sSendQueue),
                              &(psRecvCtrl->sPacket));
                    UartComm_CheckSendRequest(eDesPort);
                }
            }
        }
        // 接收字节是帧头，重新接收帧体
        else if(u8RecvByte == u8UARTCOMM_PKT_STX)
        {
            // 初始化接收控制参数
            psRecvCtrl->u8RecvByteCount = 0;
            psRecvCtrl->bEscCharActive = FALSE;
            psRecvCtrl->pu8RecvPos = &(psRecvCtrl->sPacket.u8SeqNum);
            psRecvCtrl->sPacket.u8DataLen = u8UARTCOMM_MAX_DATA_LEN;
            // 更新接收状态为接收帧体
            psRecvCtrl->eState = eSTATE_RECEIVING_BODY;
        }
    }
    // 其他
    else
    {
        // 重置接收状态
        psRecvCtrl->eState = eSTATE_WAITING_STX;
    }
}

/*******************************************************************************
  * 函数名：UartComm_ParseRecvPacket
  * 功  能：解析接收数据包
  * 参  数：u32Parm - 参数，这里传递端口
  * 返回值：无
  * 说  明：无
*******************************************************************************/
static void UartComm_ParseRecvPacket(U32 u32Parm)
{
    Bool bResult = FALSE;
    UartCommPort_te ePort;
    UartCommControl_ts *psCommCtrl;

    if(u32Parm < (U32)ePORT_SUM)
    {
        ePort = (UartCommPort_te)u32Parm;
        psCommCtrl = &(sUartComm.asCommControl[ePort]);

        // 从接收队列中取数据包
        if(psCommCtrl->sRecvQueue.u16ElementSize > 0)
        {
            Hardware_EnterCritical();    // 进入临界区
            bResult = Queue_Remove(&(psCommCtrl->sRecvQueue),
                                   &(sUartComm.sRecvPacket));
            Hardware_ExitCritical();     // 退出临界区
        }

        // 取到数据包，开始处理
        if(bResult == TRUE)
        {
            U16 u16Crc = u16UARTCOMM_PKT_CRC_SEED;

            // 计算数据包CRC
            u16Crc = CRC_CalcValue(&(sUartComm.sRecvPacket.u8SeqNum),
                                   sUartComm.sRecvPacket.u8DataLen +
                                   u8UARTCOMM_SEQ_SRC_DST_CMD_LEN_CRC_LEN,
                                   u16UARTCOMM_PKT_CRC_SEED);

            // 校验通过
            if(u16Crc == 0)
            {
                // 派遣消息
                UartComm_DispatchMsg(ePort, &(sUartComm.sRecvPacket));
            }
        }
    }
}

/***************************** 文件结束 ***************************************/
